/*
 *  theEncryptor.cpp
 *  
 *
 *  Created by B. Jones on 14/01/2010.
 *  Copyright 2010 __MyCompanyName__. All rights reserved.
 *
 */

#include "theEncryptor.h"
#include <iostream>
#include <sys/socket.h>
#include <sstream>
#include <openssl/blowfish.h>
#include <openssl/sha.h>
#include <openssl/rand.h>

theEncryptor::theEncryptor()
{

}

void
theEncryptor::blowfish(unsigned char *data, int data_len, int enc, int type)
{

	//  hash the key first! 
	unsigned char obuf[16]={0};
	SHA1((const unsigned char*)symmetricKey, 64, obuf);
	
	BF_KEY bfkey;
	int keySize = 16;
	BF_set_key(&bfkey, keySize, obuf);

	unsigned char* out=(unsigned char*) malloc(data_len);
	bzero(out,data_len);

	/*
	if(type==0){memset(ivec, 0, 16);}
	else if(type==1){memset(ivec_send, 0, 16);}
	else if(type==2){memset(ivec_rec, 0, 16);}
	*/

	int num = 0;
	if(type==0)BF_cfb64_encrypt(data, out, data_len, &bfkey, ivec, &num, enc);
	else if(type==1)BF_cfb64_encrypt(data, out, data_len, &bfkey, ivec_send, &num, enc);
	else if(type==2)BF_cfb64_encrypt(data, out, data_len, &bfkey, ivec_rec, &num, enc);
	
	memcpy(data, out, data_len);
	free(out);
}

void
theEncryptor::generateRSAKeyPair(int bits)
{
	rsa = RSA_generate_key(bits, 65537, NULL, NULL);
}


int
theEncryptor::publicEncrypt(unsigned char* toEncrypt,unsigned char* dataEncrypted,int dataLen)
{	
	return RSA_public_encrypt(dataLen, toEncrypt, dataEncrypted, rsa, RSA_PKCS1_OAEP_PADDING);
}

int
theEncryptor::encryptSymmetricKey(unsigned char* dataEncrypted,int dataLen)
{
	return publicEncrypt(symmetricKey,dataEncrypted,dataLen);
}

int
theEncryptor::privateDecrypt(unsigned char* dataEncrypted,
							 unsigned char* dataDecrypted)
{
	return RSA_private_decrypt(RSA_size(rsa), dataEncrypted, 
								   dataDecrypted, rsa, RSA_PKCS1_OAEP_PADDING);
}

int
theEncryptor::decryptSymmetricKey(unsigned char* dataEncrypted)
{
	bzero(symmetricKey,64);
	return privateDecrypt(dataEncrypted,symmetricKey);

}

void 
theEncryptor::receivePublicKeyAndSetRSA(int sock, int bits)
{
	const int max_hex_size = (bits / 4) + 1;
	char keybufA[max_hex_size];
	bzero(keybufA,max_hex_size);
	char keybufB[max_hex_size];
	bzero(keybufB,max_hex_size);
	int n = recv(sock,keybufA,max_hex_size,0); 
	n = send(sock,"OK",2,0);
	n = recv(sock,keybufB,max_hex_size,0); 
	n = send(sock,"OK",2,0);
	rsa = RSA_new();
	BN_hex2bn(&rsa->n, keybufA);
	BN_hex2bn(&rsa->e, keybufB);

}

void 
theEncryptor::transmitPublicKey(int sock, int bits)
{
	const int max_hex_size = (bits / 4) + 1;
	char keyBufferA[max_hex_size];
	char keyBufferB[max_hex_size];
	bzero(keyBufferA,max_hex_size);
	bzero(keyBufferB,max_hex_size);
	sprintf(keyBufferA,"%s\r\n",BN_bn2hex(rsa->n));
	sprintf(keyBufferB,"%s\r\n",BN_bn2hex(rsa->e));
	int n = send(sock,keyBufferA,max_hex_size,0);
	char recBuf[2];
	n = recv(sock,recBuf,2,0);
	n = send(sock,keyBufferB,max_hex_size,0);
	n = recv(sock,recBuf,2,0);
}

void
theEncryptor::generateRandomBlowfishKey(int bytes)
{
	bzero(symmetricKey,bytes);
	int n = RAND_bytes(symmetricKey, bytes);
	if(n==0)std::cout<<"Warning key was generated with shit entropy. You should not consider communication to be secure"<<std::endl;
	
}

void 
theEncryptor::sendIvec(int sock, int type)
{
	generateIV(16,type);
	if(type==0)send(sock,ivec,16,0);
	else if(type==1)send(sock,ivec_send,16,0);
	else if(type==2)send(sock,ivec_rec,16,0);
}

void
theEncryptor::receiveIvec(int sock,int type)
{
	if(type==0){memset(ivec, 0, 16);recv(sock,ivec,16,0);}
	else if(type==1){memset(ivec_send, 0, 16);recv(sock,ivec_send,16,0);}
	else if(type==2){memset(ivec_rec, 0, 16);recv(sock,ivec_rec,16,0);}

}

void
theEncryptor::generateIV(int bytes, int type)
{
	if(type==0)
	{
		memset(ivec, 0, 16);
		int n = RAND_bytes(ivec, bytes);
	}

	else if(type==1)
	{
		memset(ivec_send, 0, 16);
		int n = RAND_bytes(ivec_send, bytes);
	}

	else if(type==2)
	{
		memset(ivec_rec, 0, 16);
		int n = RAND_bytes(ivec_rec, bytes);
	}
}

void
theEncryptor::receiveEncryptedSymmetricKey(int sock)
{
	int rsaSize_ = rsaSize();
	unsigned char symEncrypted[rsaSize_];
	bzero(symEncrypted,rsaSize_);
	recv(sock,symEncrypted,rsaSize_,0);
	bzero(symmetricKey,64);
	privateDecrypt(symEncrypted,symmetricKey);

}

void
theEncryptor::encryptAndSendSymmetricKey(int sock)
{
	int rsaSize_ = rsaSize();
	// set up an array to store enciphered symmetric key and zero it out
	unsigned char symEncrypted[rsaSize_];
	bzero(symEncrypted,rsaSize_);
	encryptSymmetricKey(symEncrypted, 64);
	send(sock,symEncrypted,rsaSize_,0); // obtain connected name
}



int
theEncryptor::rsaSize()
{
	return RSA_size(rsa);
}
theEncryptor::~theEncryptor(){}
